/*
 * File    : /encode.js
 * Time    : 2021-04-25 19:24:49
 * Author  : Gray Snail
 * Version : 1.0
 * Contact : shtao2011@sina.com
 * License : Copyright (c) 2021 Gray Snail
 * Desc    : None
 */

const fs = require('fs')
const join = require('path').join

// begin: 压缩算法
function encodePolygon(coordinate, encodeOffsets) {
  var result = ''
  var prevX = quantize(coordinate[0][0])
  var prevY = quantize(coordinate[0][1])
  // Store the origin offset
  encodeOffsets[0] = prevX
  encodeOffsets[1] = prevY
  for (var i = 0; i < coordinate.length; i++) {
    var point = coordinate[i]
    result += encode(point[0], prevX)
    result += encode(point[1], prevY)
    prevX = quantize(point[0])
    prevY = quantize(point[1])
  }
  return result
}

function encode(val, prev) {
  // Quantization
  val = quantize(val)
  // Delta
  val = val - prev
  if (((val << 1) ^ (val >> 15)) + 64 === 8232) {
    //WTF, 8232 will get syntax error in js code
    val--
  }
  // ZigZag
  val = (val << 1) ^ (val >> 15)
  // add offset and get unicode
  return String.fromCharCode(val + 64)
}
function quantize(val) {
  return Math.ceil(val * 1024)
}

function encodeGeo(geo) {
  // 压缩标识
  geo.UTF8Encoding = true
  geo.UTF8Scale = 1024

  for (let f_i = 0; f_i < geo.features.length; f_i++) {
    const geometry = geo.features[f_i].geometry
    if (geometry.type === 'Polygon') {
      const coordinates = geometry.coordinates
      geometry.encodeOffsets = Array(coordinates.length)
      for (let c = 0; c < coordinates.length; c++) {
        const offset = Array(2)
        const codeStr = encodePolygon(coordinates[c], offset)
        coordinates[c] = codeStr
        geometry.encodeOffsets[c] = offset
      }
    } else if (geometry.type === 'MultiPolygon') {
      const coordinates = geometry.coordinates
      geometry.encodeOffsets = Array(coordinates.length)
      for (let c = 0; c < coordinates.length; c++) {
        const coordinate = coordinates[c]
        geometry.encodeOffsets[c] = Array(coordinate.length)

        for (let c2 = 0; c2 < coordinate.length; c2++) {
          const offset = Array(2)
          const codeStr = encodePolygon(coordinate[c2], offset)
          coordinate[c2] = codeStr
          geometry.encodeOffsets[c][c2] = offset
        }
      }
    }
  }
  return geo
}
// end: 压缩算法

// begin: 解压缩算法
function decodePolygon(coordinate, encodeOffsets, encodeScale) {
  var result = []
  var prevX = encodeOffsets[0]
  var prevY = encodeOffsets[1]

  for (var i = 0; i < coordinate.length; i += 2) {
    var x = coordinate.charCodeAt(i) - 64
    var y = coordinate.charCodeAt(i + 1) - 64
    // ZigZag decoding
    x = (x >> 1) ^ -(x & 1)
    y = (y >> 1) ^ -(y & 1)
    // Delta deocding
    x += prevX
    y += prevY

    prevX = x
    prevY = y
    // Dequantize
    result.push([x / encodeScale, y / encodeScale])
  }

  return result
}

function decodeGeo(jsonCompressed) {
  if (!jsonCompressed.UTF8Encoding) {
    return jsonCompressed
  }
  const encodeScale = jsonCompressed.UTF8Scale || 1024
  const features = jsonCompressed.features

  for (let f = 0; f < features.length; f++) {
    const feature = features[f]
    const geometry = feature.geometry

    if (geometry.type === 'Polygon') {
      const coordinates = geometry.coordinates
      for (let c = 0; c < coordinates.length; c++) {
        coordinates[c] = decodePolygon(coordinates[c], geometry.encodeOffsets[c], encodeScale)
      }
    } else if (geometry.type === 'MultiPolygon') {
      const coordinates = geometry.coordinates
      for (let c = 0; c < coordinates.length; c++) {
        const coordinate = coordinates[c]
        for (let c2 = 0; c2 < coordinate.length; c2++) {
          coordinate[c2] = decodePolygon(coordinate[c2], geometry.encodeOffsets[c][c2], encodeScale)
        }
      }
    }
  }
  // Has been decoded
  jsonCompressed.UTF8Encoding = false

  return jsonCompressed
}
// end: 解压缩算法

// 控制台预览
function viewGeo(jsonCompressed) {
  console.log('-----------------------------------------------------------------------')
  console.log(jsonCompressed)
  for (const feature of jsonCompressed.features) {
    console.log(feature.geometry)
    console.log(feature.geometry.encodeOffsets)
    if (!jsonCompressed.UTF8Encoding) {
      for (const coordinate of feature.geometry.coordinates) {
        console.log(coordinate)
      }
    }
  }
}

function main() {
  const path = '/home/xiaotao/WorkSpace/upload/map'
  const wPath = '/home/xiaotao/WorkSpace/upload/encode-map'
  let files = fs.readdirSync(path)

  files.forEach((file) => {
    const fullPath = join(path, file)
    const writeFile = join(wPath, file)

    const data = require(fullPath)
    const jsonCompressed = encodeGeo(data)
    fs.writeFile(writeFile, JSON.stringify(jsonCompressed), (err) => {
      if (err) {
        throw err
      }
      console.log(writeFile)
    })
  })

  // console.log(files)

  // const data = require('./440800000000.json');
  // const jsonCompressed = encodeGeo(data);
  // fs.writeFile('test.encode.json', JSON.stringify(jsonCompressed), (err) => {
  //   if (err) {
  //     throw err;
  //   }
  //   console.log('JSON data is saved.');
  // });
  // viewGeo(jsonCompressed);
}

main()
